package config

import (
	"cloud-stm32-cld-platform/cons"
	"cloud-stm32-cld-platform/model"
	"errors"
	"sync"

	"github.com/spf13/viper"
)

type ConfigCenter struct {
	*viper.Viper
	*sync.Mutex
}

var (
	GlobalConfigCenter = NewConfigCenter()
)

func NewConfigCenter() *ConfigCenter {
	cc := &ConfigCenter{
		viper.New(),
		&sync.Mutex{},
	}
	cc.Set(cons.DefCnfKey_PrjRepo, map[string]*model.PrjModel{})
	return cc
}

// TODO SetDefault使用整改
func (cc *ConfigCenter) Sets(pairs map[string]interface{}) {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	for k, v := range pairs {
		cc.Viper.Set(k, v)
	}
}

// TODO SetDefault使用整改
func (cc *ConfigCenter) Gets(keys []string) (res []interface{}) {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	for _, key := range keys {
		res = append(res, cc.Viper.Get(key))
	}
	return
}

func (cc *ConfigCenter) SetPrjModel(key string, val *model.PrjModel) error {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	prjRepo := cc.Viper.Get(cons.DefCnfKey_PrjRepo).(map[string]*model.PrjModel)
	if _, ok := prjRepo[key]; ok {
		return errors.New("duplicate project timestamp") // TODO ensure ts or uuid
	}
	prjRepo[key] = val
	cc.Viper.Set(cons.DefCnfKey_PrjRepo, prjRepo)
	return nil
}

func (cc *ConfigCenter) GetPrjModel(key string) *model.PrjModel {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	return cc.Viper.Get(key).(*model.PrjModel)
}

func (cc *ConfigCenter) UpdatePrjModel(prj_unicode, key string, val interface{}) error {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	prjRepo := cc.Viper.Get(cons.DefCnfKey_PrjRepo).(map[string]*model.PrjModel)
	if _, ok := prjRepo[prj_unicode]; ok {
		return errors.New("duplicate project timestamp") // TODO ensure ts or uuid
	}
	prj := prjRepo[prj_unicode]
	prj.UpdateAttr(key, val)
	return nil
}

func (cc *ConfigCenter) DelPrjModel(key string) string {
	if key == "" {
		return "empty key"
	}
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	prjRepo := cc.Viper.Get(cons.DefCnfKey_PrjRepo).(map[string]*model.PrjModel)
	if prj, ok := prjRepo[key]; ok && prj.Status == model.PRJ_BURNING {
		return "current project is burning code"
	}
	delete(prjRepo, key)
	return ""
}

func (cc *ConfigCenter) WriteCmdConf(bf *model.BaseFile) {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	cc.Viper.Set(cons.DefCnfKey_CMD_FILE, bf)
}

func (cc *ConfigCenter) WriteCmdModelConf(fm *model.FileModel) {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	cc.Viper.Set(cons.DefCnfKey_CMD_MODEL, fm)
}

func (cc *ConfigCenter) ReadCmdConf() *model.BaseFile {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	return cc.Viper.Get(cons.DefCnfKey_CMD_FILE).(*model.BaseFile)
}

func (cc *ConfigCenter) ReadCmdOptionConf() *model.FileModel {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	return cc.Viper.Get(cons.DefCnfKey_CMD_OPTION).(*model.FileModel)
}

func (cc *ConfigCenter) DelCmdModelConf() {
	cc.Mutex.Lock()
	defer func() {
		cc.Mutex.Unlock()
	}()
	cc.Viper.Set(cons.DefCnfKey_CMD_MODEL, nil)
}
